#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
	防止头文件重复包含
	#pragma once

	#include <stdio.h>

	struct EnvItem{
		char key[64];
		char value[128];
	};

	目的为了在c++能够调用C写的函数
	#ifdef __cplusplus
		extern "C" {
	#endif
		获取文件有效行数
		int getFileLines(FILE *file);
		加载配置文件
		int loadFile(const char *filePath, char ***fileData, int *line);
		解析配置文件
		int parseFile(char **fileData, int lines, struct EnvItem **envs);
		获得指定配置信息
		char* getEnvValueByKey(const char *key, struct EnvItem *envs, int lines);
		释放配置文件信息
		void destoryEnvs(struct EnvItem *item);
		判断当前行是否有效
		int isValidFileLine(const char *buf);
	#ifdef __cplusplus
		}
	#endif
*/

struct EnvItem{
	char key[64];
	char value[128];
};

//判断当前行是否有效
int isValidFileLine(const char *buf)
{
	if(NULL == buf)
	{
		return 0;
	}

	if('#' == buf[0] || '\n' == buf[0] || strchr(buf, ':') == NULL)
	{
		return 0;
	}

	return 1;
}
//获取文件有效行数
int getFileLines(FILE *file)
{
	if(NULL == file)
	{
		printf("文件指针不能为NULL\n");
		return -1;
	}

	//把文件指针重置到开头
	fseek(file, 0, SEEK_SET);

	char buf[1024] = {0};
	int lines = 0;
	while(fgets(buf, 1024, file) != NULL)
	{
		if(!isValidFileLine(buf))
		{
			continue;
		}
		++lines;
		memset(buf, 0, 1024);
	}

	//把文件指针重置到开头
	fseek(file, 0, SEEK_SET);

	return lines;
}
//加载配置文件
int loadFile(const char *filePath, char ***fileData, int *line)
{
	FILE *file = fopen(filePath, "r");
	if(NULL == file)
	{
		printf("打开文件%s失败\n", filePath);
		return -1;
	}

	int lines = getFileLines(file);

	//给每行开辟内存
	char **temp = malloc(sizeof(char *) * lines);
	if(!temp)
	{
		printf("给文件每行分配内存失败\n");
		return -2;
	}

	int index = 0;

	char buf[1024] = {0};

	while(fgets(buf, 1024, file) != NULL)
	{
		//如果返回非0为假
		if(!isValidFileLine(buf))
		{
			continue;
		}

		temp[index] = malloc(strlen(buf) + 1);
		if(!temp[index])
		{
			printf("给每行具体的数据分配内存失败\n");
			return -3;		
		}
		memset(temp[index], 0, strlen(buf)+1);
		strcpy(temp[index], buf);
		++index;
		//清空buf
		memset(buf, 0, 1024);
	}

	//关闭文件
	fclose(file);

	*fileData = temp;
	*line = lines;

	return 0;
}
//解析配置文件
int parseFile(char **fileData, int lines, struct EnvItem **envs)
{
	if(NULL == fileData)
	{
		printf("文件指针不能为空\n");
		return -1;
	}

	struct EnvItem *temp = malloc(sizeof(struct EnvItem) * lines);
	if(NULL == temp)
	{
		printf("分配envs temp内存失败\n");
		return -1;
	}
	memset(temp, 0, sizeof(struct EnvItem) * lines);

	for(int i = 0; i < lines; ++i)
	{
		char *pos = strchr(fileData[i], ':');
		strncpy(temp[i].key, fileData[i], pos-fileData[i]);
		strncpy(temp[i].value, pos+1, strlen(pos+1) - 1);

		//printf("key:%s value:%s\n", temp[i].key, temp[i].value);
	}

	//释放文件信息
	for(int i = 0; i < lines; ++i)
	{
		if(fileData[i] != NULL)
		{
			free(fileData[i]);
			fileData[i] = NULL;
		}
	}

	free(fileData);

	*envs = temp;

	return 0;
}
//获得指定配置信息
char* getEnvValueByKey(const char *key, struct EnvItem *envs, int lines)
{
	if(0 == lines || NULL == envs)
	{
		return NULL;
	}

	for(int i = 0; i < lines; ++i)
	{
		if(strcmp(key, envs[i].key) == 0)
		{
			return envs[i].value;	
		}
	}

	return NULL;
}
//释放配置文件信息
void destoryEnvs(struct EnvItem *envs)
{
	if(NULL != envs)
	{
		free(envs);
		envs = NULL;
	}
}

void testIsValidFileLine()
{
	char *testString = "#aaa";
	if(!isValidFileLine(testString))
	{
		printf("%s不是有效行\n", testString);
	}
	else
	{
		printf("%s是有效行\n", testString);
	}

	testString = "aaa";
	if(!isValidFileLine(testString))
	{
		printf("%s不是有效行\n", testString);
	}
	else
	{
		printf("%s是有效行\n", testString);
	}

	testString = "key:name";
	if(!isValidFileLine(testString))
	{
		printf("%s不是有效行\n", testString);
	}
	else
	{
		printf("%s是有效行\n", testString);
	}
}

void test()
{
	char **fileData = NULL;
	int lines = 0;

	//加载配置文件
	loadFile("./.env", &fileData, &lines);
	printf("lines:%d\n", lines);
	
	//解析配置文件
	struct EnvItem *envs = NULL;
	parseFile(fileData, lines, &envs);
	printf("ip:%s\n", getEnvValueByKey("ip", envs, lines));
	printf("ppp:%s\n", getEnvValueByKey("ppp", envs, lines));
	printf("TTT:%s\n", getEnvValueByKey("TTT", envs, lines));
	
	//释放配置信息内存
	destoryEnvs(envs);
}

int main(int argc, char const *argv[])
{
	testIsValidFileLine();
	test();
	return 0;
}
